#ifndef GRAPHICS_SHADERUNIFORMS_H
#define GRAPHICS_SHADERUNIFORMS_H

#include <graphics/node.h>
#include <math/math.h>
#include <render/texture.h>
#include <render/shader.h>
#include <core/core.h>
#include <graphics/camera.h>
#include <core/ref.h>

namespace yam3d
{
namespace graphics
{
    class ShaderUniform : public core::Object
    {
    public:
        ShaderUniform();
        virtual ~ShaderUniform();

        virtual void setUniformValue(render::ShaderProgram* shader, const core::string& name) = 0;
    private:
        ShaderUniform(const ShaderUniform&);
        ShaderUniform& operator=(const ShaderUniform&);
    };

    template <class T>
    class ShaderUniformImpl : public ShaderUniform
    {
    public:
        ShaderUniformImpl( const T& value )
            : m_value(value)
        {
        }

        virtual ~ShaderUniformImpl()
        {
        }

        virtual void setUniformValue(render::ShaderProgram* shader, const core::string& name)
        {
       //     shader->bind();
            shader->setUniformValue(name.toStdString().c_str(), m_value);
        }

        void setValue(const T& val)
        {
            m_value = val;
        }

        const T& getValue() const
        {
            return m_value;
        }
    private:
        T m_value;

        ShaderUniformImpl(const ShaderUniformImpl&);
        ShaderUniformImpl& operator=(const ShaderUniformImpl&);
    };

    template <class T>
    class ShaderSemanticUniformImpl : public ShaderUniformImpl<T>
    {
    public:
        ShaderSemanticUniformImpl( const T& value, const char* semanticName )
            : ShaderUniformImpl<T>(value)
            , m_semanticName(semanticName)
        {
        }

        virtual ~ShaderSemanticUniformImpl()
        {
        }

    private:
        core::string m_semanticName;

        ShaderSemanticUniformImpl(const ShaderSemanticUniformImpl&);
        ShaderSemanticUniformImpl& operator=(const ShaderSemanticUniformImpl&);
    };

    class ShaderUniforms : public core::Object
    {
    public:
        ShaderUniforms();
        virtual ~ShaderUniforms();

        void applyValues( render::ShaderProgram* shader );

        template <class T>
        void setUniformValue(const char *name, const T& value)
        {
            if( m_uniforms[name] == 0 )
            {
                m_uniforms[name] = new ShaderUniformImpl<T>(value);
            }
            else
            {
                ShaderUniformImpl<T>* uniform = dynamic_cast<ShaderUniformImpl<T>*>(m_uniforms[name].ptr());
                assert( uniform != 0 );
                uniform->setValue(value);
            }
        }

        template <class T>
        void setUniformSemanticValue(const char *name, const T& value, const char* semanticName)
        {
            if( m_uniforms[name] == 0 )
            {
                m_uniforms[name] = new ShaderSemanticUniformImpl<T>(value,semanticName);
            }
            else
            {
                ShaderUniformImpl<T>* uniform = dynamic_cast<ShaderUniformImpl<T>*>(m_uniforms[name].ptr());
                assert( uniform != 0 );
                uniform->setValue(value);
            }
        }

        template <class T>
        void getUniformValue(const char *name, T* value)
        {
            ShaderUniformImpl<T>* uniform = dynamic_cast<ShaderUniformImpl<T>*>(m_uniforms[name]);
            assert( uniform != 0 );
            return uniform->getValue();
        }

        template <class T>
        T getUniformValue(const char* name) const
        {
            T res;
            getUniformValue(name,&res);
            return res;
        }

    private:
        typedef core::hash<core::string, core::Ref<ShaderUniform> > HashType;
        HashType m_uniforms;

     //   typedef core::hash<core::string, ShaderUniform*> SemanticHashType;
     //   SemanticHashType m_semanticUniforms;

        ShaderUniforms(const ShaderUniforms&);
        ShaderUniforms& operator=(const ShaderUniforms&);
    };

}
}

#endif // GRAPHICS_SHADERUNIFORMS_H
